use deno_core::{op_print, serde_v8, v8, Extension, JsRuntime, Op, RuntimeOptions};
use serde::Serialize;
use std::{
    fmt::Debug,
    ops::{Deref, DerefMut},
};
use QcCore::resources::ResourceManager;
use QcTools::utils::r#ref::Ref;

use super::{
    audio::{opPauseAudio, opPlayAudio, opPlayAudioClip},
    button::{opCreateButton, opGetButtonText, opSetButtonText},
    canvas::{opAddChild, opAddChildWithParent, opRemoveChild},
    label::{opCreateLabel, opGetLabelText, opSetLabelText},
    opGetPosition, opGetRotation, opGetScale, opGetUiNode, opSetMaterial, opSetPosition,
    opSetRotation, opSetScale, opTranslate, op_addComponent, op_createGameObject, op_getComponent,
    op_getComponentById, op_getGameObject, op_getGameObjectById,
    panel::{opGetPanelOrientation, opGetPanelSpacing, opSetPanelOrientation, opSetPanelSpacing},
    resource::{opLoadAudio, opLoadJson, opLoadMaterial, opLoadMesh, opLoadRes},
    scene::{opLoadScene, opLoadSceneFromName, opPreloadScene},
    textbox::{opGetTextBoxHintText, opGetTextBoxText, opSetTextBoxHintText, opSetTextBoxText},
    widget::{
        opGetWidgetActive, opGetWidgetHeight, opGetWidgetWidth, opSetWidgetActive,
        opSetWidgetHeight, opSetWidgetWidth,
    },
};

pub struct JsRuntimeManager {
    js: JsRuntime,
}

impl Debug for JsRuntimeManager {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Debug").finish()
    }
}

impl Deref for JsRuntimeManager {
    type Target = JsRuntime;

    fn deref(&self) -> &Self::Target {
        &self.js
    }
}

impl DerefMut for JsRuntimeManager {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.js
    }
}

impl JsRuntimeManager {
    pub fn new() -> Self {
        let ext = Extension {
            name: "QcScript",
            ops: std::borrow::Cow::Borrowed(&[
                op_addComponent::DECL,
                op_getComponent::DECL,
                op_getComponentById::DECL,
                op_getGameObject::DECL,
                op_getGameObjectById::DECL,
                op_createGameObject::DECL,
                // Transform
                opGetPosition::DECL,
                opSetPosition::DECL,
                opGetRotation::DECL,
                opSetRotation::DECL,
                opGetScale::DECL,
                opSetScale::DECL,
                opTranslate::DECL,
                // ResourceManager
                opLoadRes::DECL,
                opLoadJson::DECL,
                opLoadMaterial::DECL,
                opLoadMesh::DECL,
                opLoadAudio::DECL,
                // MaterialRender
                opSetMaterial::DECL,
                // AudioEngine
                opPlayAudio::DECL,
                opPlayAudioClip::DECL,
                opPauseAudio::DECL,
                // Scene
                opLoadScene::DECL,
                opLoadSceneFromName::DECL,
                opPreloadScene::DECL,
                // Canvas
                opAddChild::DECL,
                opAddChildWithParent::DECL,
                opRemoveChild::DECL,
                // UiNode
                opGetUiNode::DECL,
                // Widget
                opGetWidgetActive::DECL,
                opSetWidgetActive::DECL,
                opGetWidgetWidth::DECL,
                opSetWidgetWidth::DECL,
                opGetWidgetHeight::DECL,
                opSetWidgetHeight::DECL,
                // Button
                opGetButtonText::DECL,
                opSetButtonText::DECL,
                opCreateButton::DECL,
                // Label
                opGetLabelText::DECL,
                opSetLabelText::DECL,
                opCreateLabel::DECL,
                // Panel
                opGetPanelOrientation::DECL,
                opSetPanelOrientation::DECL,
                opGetPanelSpacing::DECL,
                opSetPanelSpacing::DECL,
                // TextBox
                opGetTextBoxText::DECL,
                opSetTextBoxText::DECL,
                opGetTextBoxHintText::DECL,
                opSetTextBoxHintText::DECL,
            ]),
            ..Default::default()
        };
        let runtime = JsRuntime::new(RuntimeOptions {
            extensions: vec![ext],
            ..Default::default()
        });

        Self { js: runtime }
    }

    pub fn update(&mut self) {
        self.execute_script_static("update", "globalThis.__Update__()")
            .unwrap();
    }
}

#[cfg(test)]
mod test {
    use deno_core::{serde_json, v8, Extension, JsRuntime, RuntimeOptions};

    #[test]
    pub fn serialize() {
        let ext = Extension {
            name: "QcScript",
            ops: std::borrow::Cow::Borrowed(&[]),
            ..Default::default()
        };
        let mut runtime = JsRuntime::new(RuntimeOptions {
            extensions: vec![ext],
            ..Default::default()
        });

        let result = runtime
            .execute_script_static(
                "ov",
                "let obj={a:'233333',b:333,show(){return this.b;}};obj",
            )
            .unwrap();
        let scope = &mut runtime.handle_scope();
        // let result = result.open(runtime.v8_isolate());
        let result = v8::Local::<v8::Value>::new(scope, result);
        let result: serde_json::Value = serde_v8::from_v8(scope, result).unwrap();
        let jsValue = serde_v8::to_v8(scope, result).unwrap();
        let showName = v8::String::new(scope, "show").unwrap();
    }

    #[test]
    pub fn deserialize() {}
}
